package view;

import gamecomponents.graphic.BlitData;
import gamecomponents.settings.GameSettings;
import gamemodels.IGameModel;
import gamemodels.WorldModel;

import java.awt.Canvas;
import java.awt.Color;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JFrame;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.SimpleVector;

/**
 * Our view. Obeserves models for changes, and redraws when necessary.
 * @author Team Deer
 * 
 */
public class View extends JFrame implements Observer {

	private static final long serialVersionUID = 2249364206145347895L;

	private static View singletonInstance;

	private final Canvas canvas;
	private final FrameBuffer buffer;

	// Constructor and singleton getter
	
	private View() {

		final SimpleVector screenSize = GameSettings.SCREEN_SIZE;
		this.setUndecorated(true);
		this.setSize((int) screenSize.x, (int) screenSize.y);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		this.buffer = new FrameBuffer((int) screenSize.x, (int) screenSize.y,
				FrameBuffer.SAMPLINGMODE_HARDWARE_ONLY);
		this.buffer.setBlittingTarget(FrameBuffer.BLITTING_TARGET_FRONT);

		this.canvas = this.buffer.enableGLCanvasRenderer();
		this.buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
		this.buffer.optimizeBufferAccess();

		this.add(this.canvas);
		this.setVisible(true);
	}

	/**
	 * 
	 * @return - The one and only instance of a view
	 */
	public static View getInstance() {
		if (singletonInstance == null) {
			singletonInstance = new View();
		}
		return singletonInstance;
	}

	
	// Methods
	
	/**
	 * Loads a model for the view to observe
	 * 
	 * @param model
	 *            - The model to observe
	 */
	public void load(final IGameModel model) {
		((Observable) model).addObserver(this);
	}

	/**
	 * Draws the overlay over the world
	 * 
	 * @param model
	 *            - The model to collect data to draw from
	 */
	private void drawOverlay(final List<BlitData> blitData) {
		for (final BlitData temp : blitData) {
			this.buffer.blit(
					temp.getTexture(), // texture
					temp.getSrcX() /* srcX */, temp.getSrcY() /* srcY */,
					temp.getDestX()/* destX */, temp.getDestY() /* destY */,
					temp.getSrcWidth() /* sourceWidth */,
					temp.getSrcHeight() /* sourceHeight */,
					temp.getDestWidth() /* destWidth */,
					temp.getDestHeight() /* destHeight */,
					temp.getTransValue() /* transValue */,
					temp.isAdditive() /* additive */, temp.getAddColor()); /* addColor */
		}
	}

	@Override
	public void dispose() {
		this.buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
		this.buffer.dispose();
		super.dispose();
	}

	/**
	 * 
	 * @return - The canvas
	 */
	public Canvas getCanvas() {
		return this.canvas;
	}

	@Override
	public void update(final Observable caller, final Object arg) {
		this.buffer.clear(Color.blue);

		// If the caller is a WorldModel you have to render the scene
		if (caller instanceof WorldModel) {
			((WorldModel) caller).getWorld().renderScene(this.buffer);
			((WorldModel) caller).getWorld().draw(this.buffer);
		}

		this.buffer.update();
		this.drawOverlay(((IGameModel) caller).getBlitData());
		this.buffer.displayGLOnly();
		this.canvas.repaint();
	}

}
